/* 
 * Copyright (c)	Insighten Intelligent Equipment co.,LTD
 * 
 *  Use of this software is controlled by the terms and conditions found
 *  in the license agreement under which this software has been supplied.
 * 
 */
/** 
 * @file tcp_server.c
 * @brief tcp server library for create tcp socket
 * @author taco [tacoee@gmail.com]
 * @version 0.1
 * @date 2010-09-08
 */

#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>

#include "tcp_server.h"

TcpServer *tcp_server_new(void)
{
	TcpServer *thiz;

	thiz = calloc(sizeof(TcpServer), 1);

	return thiz;
}
Ret tcp_server_destroy(TcpServer *thiz)
{
	if (thiz != NULL) {
		if (thiz->fd != -1) {
			close(thiz->fd);
		}
		free(thiz);
		return RET_OK;
	}

	return RET_NO_TARGET;
}

Ret tcp_server_init(TcpServer *thiz, int port, int max, SocketAttr *attr)
{
	int i;

	if (thiz != NULL && max > 0) {
		thiz->fd = -1;
		thiz->port = port;
		memset(&thiz->addr, 0, sizeof(thiz->addr));
		thiz->addr.sin_family = AF_INET;
		thiz->addr.sin_addr.s_addr = INADDR_ANY;
		thiz->addr.sin_port = htons(port);
		thiz->addr_len = sizeof(thiz->addr);
		thiz->max_con = max;
		thiz->con_cnt = 0;
		thiz->attr = attr;
		return RET_OK;
	}

	return RET_NO_TARGET;
}

Ret tcp_server_create(TcpServer *thiz)
{
	int ret = -1;
	if (thiz != NULL) {
		thiz->fd = socket(AF_INET, SOCK_STREAM, 0);
		if (thiz->fd == -1) {
			return RET_QUIT;
		}
		ret = set_sock_opt(thiz->fd, thiz->attr);
		if (ret < 0) {
			return RET_IGNORED;
		}
		ret = bind(thiz->fd, (struct sockaddr *)&thiz->addr, thiz->addr_len);
		if (ret < 0) {
			return RET_FAIL;
		}
		ret = listen(thiz->fd, thiz->max_con);
		if (ret < 0) {
			return RET_FAIL;
		}
	}
	return RET_OK;
}

Request *request_new()
{
	Request *req = NULL;

	req = calloc(sizeof(Request), 1);

	req->next = NULL;
	req->prev = NULL;
	req->method = M_NO;

	return req;
}

Ret request_free(Request *thiz)
{
	if (thiz != NULL) {
		thiz->next = NULL;
		thiz->prev =NULL;
		free(thiz);

		return RET_OK;
	}

	return RET_OUT_OF_SPACE;
}

void request_enqueue(Request **head, Request *req)
{
	if (*head) {
		(*head)->prev = req;
	}

	req->next = *head;
	req->prev = NULL;

	*head = req;
}

void request_dequeue(Request **head, Request *req)
{
	if (*head == req) {
		*head = req->next;
	}
	if (req->prev) {
		req->prev->next = req->next;
	}
	if (req->next) {
		req->next->prev = req->prev;
	}
	req->next = NULL;
	req->prev = NULL;
}

Request *tcp_server_request(TcpServer *thiz, SocketAttr *cli_attr)
{
	Request *req = NULL;
	int opt;
	int len;
	int ret = -1;

	if (thiz == NULL) {
		return NULL;
	}
	
	req = request_new();
	if (req != NULL) {
		req->clilen = sizeof(req->cliaddr);
		req->fd = accept(thiz->fd,&req->cliaddr, &req->clilen);
		if (req->fd < 0) {
			goto err;
		}

		if (cli_attr != NULL) {
			if (set_sock_opt(req->fd, cli_attr) < 0) {
				goto err;
			}
			req->attr = cli_attr;
		}
		thiz->con_cnt++;
	}
	return req;
err:
	if (req->fd != -1) {
		close(req->fd);
	}
	if (req != NULL) {
		free(req);
	}
	return NULL;
}

Ret tcp_server_req_buf_init(Request *thiz, RwBuf *buf)
{
	if (buf == NULL || thiz == NULL) {
		return RET_FAIL;
	}
	thiz->buf = buf;

	return RET_OK;
}

Ret tcp_server_dis_client(Request *thiz, TcpServer *svr)
{
	if (thiz != NULL && svr != NULL) {
		if (thiz->fd != -1) {
			close(thiz->fd);
		}
		thiz->buf = NULL;
		free(thiz);
		svr->con_cnt--;
		return RET_OK;
	}
	return RET_FAIL;
}

int tcp_server_read(Request *req, int size)
{
	int ret = 0;

	if (req != NULL && req->buf != NULL) {
		ret = read(req->fd, req->buf->mem + req->buf->wpos, size);	
		if (ret < 0) {
			if (errno == EAGAIN || errno == EWOULDBLOCK) {
				return -2;
			} else {
				return -1;
			}
		}
	}
	
	return ret;
}

int tcp_server_write(Request *req, int size)
{
	int ret = 0;

	if (req != NULL && req->buf != NULL) {
		ret = write(req->fd, req->buf->mem + req->buf->rpos, size);
		if (ret < 0) {
			if (errno == EAGAIN || errno == EWOULDBLOCK) {
				return -2;
			} else {
				return -1;
			}
		}
	}

	return ret;
}

Command *command_new()
{
	Command *thiz = NULL;

	thiz = calloc(sizeof(Command), 1);

	return thiz;
}

Ret command_destory(Command *thiz)
{
	if (thiz != NULL) {
		memset(thiz->cmd_name, 0, COMMAND_LEN);
		memset(thiz->cmd_val, 0, COMVAL_LEN);
		thiz->prev = NULL;
		thiz->next = NULL;
		return RET_OK;
	}

	return RET_FAIL;
}
void command_enqueue(Command **head, Command *cmd)
{
	if (*head) {
		(*head)->prev = cmd;
	}

	cmd->next = *head;
	cmd->prev = NULL;

	*head = cmd;
}

void command_dequeue(Command **head, Command *cmd)
{
	if (*head == cmd) {
		*head = cmd->next;
	}
	if (cmd->prev) {
		cmd->prev->next = cmd->next;
	}
	if (cmd->next) {
		cmd->next->prev = cmd->prev;
	}
	cmd->next = NULL;
	cmd->prev = NULL;
}
